package com.i360day.invoker.proxy;

import com.i360day.invoker.hystrix.SetterFactory;
import com.i360day.invoker.properties.HttpInvokerHystrixProperties;
import com.i360day.invoker.properties.HttpInvokerProperties;
import com.netflix.hystrix.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author liju.z
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HttpInvokerHystrixProperties.class)
@ConditionalOnProperty(value = "spring.invoker.hystrix.enabled", matchIfMissing = true)
public class HystrixProxyConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Targeter hystrixTargeter(SetterFactory setterFactory) {
        return new HystrixTargeter(setterFactory);
    }

    @Bean
    @ConditionalOnMissingBean
    public SetterFactory setterFactory(HttpInvokerHystrixProperties httpInvokerHystrixProperties, HttpInvokerProperties httpInvokerProperties) {
        HystrixCommandProperties.Setter setGet = httpInvokerHystrixProperties.getSetGet();
        Integer timeout = Optional.ofNullable(setGet.getExecutionTimeoutInMilliseconds()).orElse(httpInvokerProperties.getReadTimeout());
        setGet.withExecutionTimeoutInMilliseconds(timeout);

        HystrixThreadPoolProperties.Setter threadSetter = httpInvokerHystrixProperties.getThreadSetter();

        return (target, method) -> {

            String commandKey = String.format("%s#%s(%s)", method.getDeclaringClass().getName(), method.getName(), Stream.of(method.getParameterTypes()).map(m -> m.toString()).collect(Collectors.joining(",")));

            return HystrixCommand.Setter
                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey(target.getName()))
                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(commandKey))
                    .andCommandPropertiesDefaults(setGet)
                    .andThreadPoolPropertiesDefaults(threadSetter)
                    .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
        };
    }
}
